package com.demo.shiro;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfiguration {
	/**
	 * 设置shiro拦截器
	 * 
	 * @param securityManager
	 * @return
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {

		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

		// 设置securityManager（必须）
		shiroFilterFactoryBean.setSecurityManager(securityManager);

		// 设置拦截器
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

		// 设置登录URL,这里貌似必须是接口。不能直接到jsp页面
		shiroFilterFactoryBean.setLoginUrl("/login/toLogin");
		// 登陆成功后要进入的接口
		shiroFilterFactoryBean.setSuccessUrl("/login/toLoginSuccesss");
		// 未授权要进入的接口
		shiroFilterFactoryBean.setUnauthorizedUrl("/login/goNoPermission");
		// src="jquery/jquery-3.2.1.min.js" 生效
		filterChainDefinitionMap.put("/jquery/*", "anon");
		// 对登陆验证不生效
		filterChainDefinitionMap.put("/login/*", "anon");
		// 对异常类不生效
		filterChainDefinitionMap.put("/Exception.class", "anon");

		// 对其他所有url生效（这句话不清楚和最后一个拦截器是否效果一样）
		// filterChainDefinitionMap.put("/*", "authc");
		// 退出过滤器
		filterChainDefinitionMap.put("/logout", "logout");

		// 设置角色的访问权限的路径，如果设置错误则无法执行MyShiroRealm里的doGetAuthorizationInfo
		//roles代表角色，perms代表着权限
		 filterChainDefinitionMap.put("/user/goUser", "roles[user],perms[user]");
		 filterChainDefinitionMap.put("/admin/goAdmin", "roles[admin],perms[admin]");
		// 最后是固定的
		filterChainDefinitionMap.put("/**", "authc");
		// 将自定义规则加入
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		// 返回拦截规则
		return shiroFilterFactoryBean;
	}

	/**
	 * 
	 * 凭证匹配器 （由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
	 * 所以我们需要修改下doGetAuthenticationInfo中的代码; ) 就相当于设置密码的加密格式
	 *
	 * @return
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		// 设置密码md5散列算法的算法
		hashedCredentialsMatcher.setHashAlgorithmName("md5");
		// 设置MD5算法的散列次数,1024相当于md5(md5())
		hashedCredentialsMatcher.setHashIterations(1024);
		return hashedCredentialsMatcher;
	}

	@Bean
	public MyShiroRealm myShiroRealm() {
		MyShiroRealm myShiroRealm = new MyShiroRealm();
		// 设置加密格式
		myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return myShiroRealm;
	}

	@Bean
	public DefaultWebSecurityManager securityManager() {
		DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
		// 注入自己的realm
		defaultWebSecurityManager.setRealm(myShiroRealm());
		// 注入缓存管理器
		defaultWebSecurityManager.setCacheManager(ehCacheManager());
		return defaultWebSecurityManager;
	}

	/**
	 * 开启shiro aop注解支持 使用代理方式;所以需要开启代码支持; 权限验证要aop支持
	 * 
	 * @param securityManager
	 * @return
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
			DefaultWebSecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	/**
	 * DefaultAdvisorAutoProxyCreator，Spring的一个bean，由Advisor决定对哪些类的方法进行AOP代理。
	 * （不太理解与authorizationAttributeSourceAdvisor（）的区别在哪）
	 * 
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator defaultAPP = new DefaultAdvisorAutoProxyCreator();
		defaultAPP.setProxyTargetClass(true);
		return defaultAPP;
	}

	/**
	 * shiro缓存管理器，需要注入到securityManager中
	 * 
	 * @return
	 */
	@Bean
	public EhCacheManager ehCacheManager() {
		EhCacheManager ehCacheManager = new EhCacheManager();
		ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
		return ehCacheManager;
	}

}
